#include <stdio.h>
#include "ctrl.h"
#include <curl/curl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <sys/sendfile.h>
#include <signal.h>
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <time.h>
#include <openssl/sha.h>
#include "configure.h"
#include "url_info.h"
#include "file_answer.h"
#include "disk_answer.h"
#include "file_mgr.h"
#include "stat.h"
#include "admin.h"
#include "test_curl.h"
#include "flv.h"
//#define MSGLOG


#define FD_TIMEOUT 4 // seconds
#define ALARM_VALUE 60*20 // 20 minuts
#define EXPIRE_TIME 60*15
#define MYRAND(s) ((u_int)((s*1103515245+12345)/65536) % 32768)

extern char buff_404[]; 
extern int buff_404_len;
time_t last_send_time = 0;



void daemon_chg();
void send_data(int fid, int socket_id, struct dl_task *dl_task);
inline void send_404(int sock);
size_t get_head( void *ptr, size_t size, size_t nmemb, void *userdata);
inline int file_exist(char *pathname);
void* http_recei_requst(int socket_id);
int create_dir(char *pathname);
int create_relative_path(struct url_info *url);
void openlog();
inline void closelog();
void msglog(char *msg);
#define N_CONF 12
#define CONF_UPDATE_INTEVAL (60*5)
struct conf confs[N_CONF] = {{0,}, };
struct conf *gconf = &confs[0];
void* update_conf(void* parm)
{
	u_int i = 0;
	while (1)
	{
		sleep(CONF_UPDATE_INTEVAL);
		i = (i+1)%N_CONF;
		free_config(&confs[i]);
		read_config_file(&confs[i]);
		gconf = &confs[i];
	}
}
void create_update_conf_thread()
{
	pthread_t t;
	if (pthread_create(&t, 0, update_conf, 0) != 0)
	{
		perror("pthread_create");
		_exit(0);
	}
}
inline void handler(int i)
{
	if (time(0)-last_send_time > EXPIRE_TIME)
		_exit(0);
}
void start_cleaner_task()
{
	int i;
	struct conf *conf = gconf;
	for(i=0; i<conf->nroot_dir; i++)
	{
		if(fork() == 0){   
			while(1){
				clean_disk(conf->root_dir[i]);
				sleep(10);
			}
			_exit(0);
		}
	}
}
#define SHM_KEY 99
#define SHM_SIZE sizeof(struct cache_stat)
struct cache_stat *cache_stat;
void create_share_stat()
{
	int shmid;
	char *shmptr;

	if ((shmid = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT)) == -1 )
	{
		perror("shmget");
		_exit(0);
	}

	if ((shmptr = shmat(shmid, 0, 0)) == (void *)-1 )
	{
		perror("shmat");
		_exit(0);
	}

	cache_stat = (struct cache_stat*)shmptr;
	if (stat_init(cache_stat) < 0)
	{
		printf("stat_init error\n");
		_exit(0);
	}
}

void prepare_dir()
{
	int i, j, k;
	char buff[MAX_PATH_PREFIX];
	char *p1, *p2;
	struct conf *conf = gconf;
	for (i=0; i<conf->nroot_dir; i++) {
		memcpy(buff, conf->root_dir[i], conf->root_dir_len[i]);
		p1 = buff + conf->root_dir_len[i];
		for (j=0; j<256; j++) {
			p2 = p1 + sprintf(p1, "/%02X/", j);
			create_dir(buff);
			printf("%s\n", buff);
			for (k=0; k<256; k++) {
				sprintf(p2, "%02X/", k);
				create_dir(buff);
			}
		}
	}
}

//#define PRINT 1
int ffmpeg_convert(int argc, char **argv);
void ffmpeg_init();
void* disk_answer_proc(void *parm);
int main(int argc, char **argv)
{

	int sock_lsn, sock_peer, pri=0;
	struct sockaddr_in addr_lsn;

	printf("main pid: %d\n", getpid());
#ifdef PRINT
	pri = 1;
#endif
	if(!pri && argc == 1) 
		daemon_chg();
	signal(SIGPIPE, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
	umask(0);
	buff_404_len = strlen(buff_404);

	read_config_file(&confs[0]);

	/* 
	 * this may take several miutes 
	 */
	if (argc > 1) {
		prepare_dir();
		printf("complete\n");
		return 0;
	}



	create_update_conf_thread();


#ifdef MSGLOG
	openlog();
#endif
	memset(&addr_lsn, 0, sizeof(addr_lsn));
	addr_lsn.sin_family = AF_INET;
	addr_lsn.sin_port = htons(80);
	addr_lsn.sin_addr.s_addr = htonl(INADDR_ANY);
	if ((sock_lsn = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("socket() error\n");
		return 0;
	}
	if (bind(sock_lsn, (struct sockaddr*)&addr_lsn, (socklen_t)sizeof(addr_lsn)) < 0)
	{
		printf("bind() fail\n");
		return 0;
	}

	if (listen(sock_lsn, 20) < 0)
	{
		printf("listen error\n");
		return 0;
	}


	/* 
	 * create_share_stat() must be called before others
	 * task are created 
	 */
	create_share_stat();
	start_file_answer();
	start_disk_answer();
	start_cleaner_task();
	start_admin();
	while (1)
	{
		pid_t pid;
		if ((sock_peer = accept(sock_lsn, 0, 0)) < 0)
		{
			continue;
		}
		if ((pid = fork()) == 0)
		{
			signal(SIGALRM, handler);
			alarm(ALARM_VALUE);
			close(sock_lsn);
			http_recei_requst(sock_peer);
			close(sock_peer);
#ifdef MSGLOG
			closelog();
#endif
			_exit(0);
		} else if (pid != -1) {
			close(sock_peer);
		} else {
			perror("fork");
		}
	}
	return 0;
}

int msgfid;
void openlog()
{
	msgfid = open("/log", O_WRONLY | O_CREAT | O_APPEND);
}
inline void closelog()
{
	if (msgfid >= 0)
		close(msgfid);
}
void msglog(char *msg)
{
	if (msgfid >= 0)
		write(msgfid, msg, strlen(msg));
}

struct curl_slist* make_slist(struct url_info *url_info, struct curl_slist *slist)
{
	int i;
	for (i=0; i<url_info->hnum; i++)
		slist = curl_slist_append(slist, url_info->hline[i]);
	return slist;
}
int download_data(struct url_info *url_info, int socket_id, struct conf *conf)
{
	FILE *f;
	CURL *curl;
	CURLcode res;
	struct curlblock blk;
	struct disk_fmt disk;
	char *file_path = url_info->file_path;
	time_t stime, etime;

	if (!(f = fopen(file_path, "w+")))
	{
		return 0;
	}

	if (!(curl=curl_easy_init()))
	{
		fclose(f);
		return 0;
	}
	conf->slist = make_slist(url_info, conf->slist);

	disk.ver = 1;
	disk.magic = DISK_MAGIC;
	disk.filelen = 0;
	disk.headlen = 0;

	blk.sock = socket_id;
	blk.flag = 0;
	blk.f = f;
	blk.url = url_info;
	blk.disk = &disk;

	curl_easy_setopt(curl, CURLOPT_URL,url_info->url);
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
	curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &blk);
	curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_head);
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, conf->slist);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
	curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 3);

	stime = time(0);
	res=curl_easy_perform(curl);
	curl_easy_cleanup(curl);
	etime = time(0);

	if (res)
	{
		fclose(f);
		remove(file_path);
#ifdef MSGLOG
		msglog("-");
		msglog(url_info->url);
		msglog("\n");
#endif
	} else 
	{
		fclose(f);
	}

	stat_lock(cache_stat);
	if (res)
	{
		cache_stat->fail ++;
		cache_stat->item[cache_stat->index].success = 0;
	} else 
	{
		cache_stat->success ++;
		cache_stat->item[cache_stat->index].success = 1;
	}
	cache_stat->item[cache_stat->index].size = disk.filelen;
	cache_stat->item[cache_stat->index].time = etime - stime;
	strcpy(cache_stat->item[cache_stat->index].url, url_info->url);

	cache_stat->index = (cache_stat->index + 1) % MAX_ENTRY;
	stat_unlock(cache_stat);

	return !res;
}

inline int file_exist(char *pathname)
{
	return access(pathname, F_OK) == 0 ? 1 : 0;
}

/* 0 success, -1 fail */
int create_dir(char *pathname)
{
	if (access(pathname, F_OK) == -1)
		return mkdir(pathname, 0777);
	return 0;
}

int get_range(char *range, unsigned int *from, unsigned int *to, unsigned int file_len)
{
	*from = *to = 0;
	int k = 0;
	if (!range)
	{
		*from = 0;
		*to = file_len;
		return 1;
	}
	if (sscanf(range, "Range: bytes=-%u", to) < 1 &&
			(k = sscanf(range, "Range: bytes=%u-%u", from, to)) < 1)
		return -1;
	if (*from == 0 && k < 2)
	{
		if (*to > file_len)
			return -1;
		*from = file_len - *to;
		*to = file_len;
	} else
	{
		*to = *to ? (*to+1) : file_len;
		if (*from >= *to)
			return -1;
		if (*to > file_len)
			return -1;
	}
	return 1;
}

inline int file_fresh(struct stat *st)
{
	return time(0) - st->st_mtime < 60*20;
}

int should_omit(char *para, struct conf *conf, struct url_info *url_info)
{
	char buff[1500];
	unsigned int u;
	int i;
	for (i=0; i<conf->nkey; i++)
		if (strstr(para, conf->key[i]))
			return 1;

	/* now look for start and end parameter */
	if (sscanf(para, "%[A-Za-z=]%u", buff, &u) != 2)
		return 0;
	for (i=0; i<conf->nstart; i++)
		if (strstr(buff, conf->start[i])) {
			url_info->start = u;
			return 1;
		}
	for (i=0; i<conf->nend; i++)
		if (strstr(buff, conf->end[i])) {
			url_info->end = u;
			return 1;
		}

	return 0;
}
int url_parameter_filter(char *para_str, char *dst, struct conf *conf, struct url_info *url_info)
{
	u_int len;
	u_int i;
	char *para[50];
	u_int npara;
	npara = split_r(para, para_str, "&");
	for (i=0; i<npara; i++)
	{
		if (!should_omit(para[i], conf, url_info))
		{
			len = strlen(para[i]);
			memmove(dst, para[i], len);
			dst[len] = '&';
			dst += len+1;
		}
	}
	*(dst-1) = 0;

	return 1;
}

// return prefix len deleted, 0 meaning nothing deleted
u_int del_prefix(struct url_info *url_info, struct conf *conf)
{
	u_int ret = 0;
	u_int filename_len = url_info->filenamelen;
	char *filename = url_info->filename;

	if (conf->prefix_len == 0 || filename_len <= conf->prefix_len)
		return ret;

	if (strncmp(filename, conf->buffprefix, conf->prefix_len) == 0)
	{
		memmove(filename, filename+conf->prefix_len, filename_len-conf->prefix_len+1);
		url_info->filenamelen -= conf->prefix_len;
		return conf->prefix_len;
	}

	return ret;
}
int rebuild_url(struct url_info *url_info, struct conf *conf)
{
	u_int del_len;
	int ret = 1;
	char *old_mark;

	del_len = del_prefix(url_info, conf);
	if (url_info->qmark)
	{ 
		old_mark = url_info->qmark;
		url_info->qmark -= del_len;
		ret = url_parameter_filter(old_mark+1, url_info->qmark+1, conf, url_info);
		if (*url_info->qmark == 0)
			url_info->qmark = 0;
	}
	return ret;
}

int range_satisfied(struct dl_task *dl_task, struct disk_fmt *disk, struct stat *st)
{
	return dl_task->from + sizeof(struct disk_fmt) + disk->headlen < st->st_size;
}

int relative_path_exist(struct url_info *url_info, u_int rnum, struct conf *conf)
{
	int i, ret = 0;
	u_int n;
	char *file_path;
	for (i=0; i<conf->nroot_dir; i++)
	{
		n = (rnum+i)%conf->nroot_dir;
		url_info->file_path = url_info->file_path_relative - conf->root_dir_len[n];
		memcpy(url_info->file_path, conf->root_dir[n], conf->root_dir_len[n]);
		if (file_exist(url_info->file_path))
			return 1;
	}
	return ret;
}

void assign_path_prefix(struct url_info *url_info, u_int rnum, struct conf *conf)
{
	u_int n = conf->ava_root_dir[rnum%conf->nava];
	url_info->file_path = url_info->file_path_relative - conf->root_dir_len[n];
	memcpy(url_info->file_path, conf->root_dir[n], conf->root_dir_len[n]);
}

int create_full_path(struct url_info *url_info, struct conf *conf)
{
	int ret;
	char *url;
	char *loc, *loc2;
	unsigned int n;


	if (!create_relative_path(url_info)) 
		return 0;

	n = conf->map[*(unsigned char*)url_info->file_path_relative % conf->nmap_str];
	url_info->file_path = url_info->file_path_relative - conf->root_dir_len[n];
	memcpy(url_info->file_path, conf->root_dir[n], conf->root_dir_len[n]);
	return 1;
}

int is_flv(char *name, int len) {
	if (len > 4 && *(u_int32_t*)(name + len - 4) == *(u_int32_t*)".flv")
		return 1;
	return 0;
}	
int is_mp4(char *name, int len) {
	if (len > 4 && *(u_int32_t*)(name + len - 4) == *(u_int32_t*)".mp4")
		return 1;
	return 0;
}	

struct timeval tvsnd = {10,0}; 
struct timeval tvrcv = {30,0}; 
void* http_recei_requst(int socket_id)
{
	struct url_info url;
	int fid = -1;
	u_int rnum;
	u_int exist;

	url.start = url.end = 0;

	if (setsockopt(socket_id, SOL_SOCKET,SO_SNDTIMEO, (char *)&tvsnd,sizeof(struct timeval)) < 0)
		perror("setsockopt");
	
	if (setsockopt(socket_id, SOL_SOCKET,SO_RCVTIMEO, (char *)&tvrcv,sizeof(struct timeval)) < 0)
		perror("setsockopt");

	if (recv_header(socket_id, &url) < 0) {
		send_404(socket_id);
		goto out;
	}

	if (process_header_line(&url) < 0) {
		send_404(socket_id);
		goto out;
	}

	if (locate_filename_and_qmark(&url) < 0) {
		send_404(socket_id);
		goto out;
	}

	/* 
	 * after the url rebuild, the url.start and url.end are filled,
	 * if any, that is: url.start != url.end
	 */
	if (rebuild_url(&url, gconf) < 0) {
		send_404(socket_id);
		goto out;
	}

	if (gconf->use_map) {
		if (create_full_path(&url, gconf) < 0) {
			send_404(socket_id);
			goto out;
		}
		exist = file_exist(url.file_path);
	} else {
		rnum = MYRAND(getpid())%100;
		if (create_relative_path(&url) < 0) {
			send_404(socket_id);
			goto out;
		}
		exist = relative_path_exist(&url, rnum, gconf);
	}

	if (exist) {
		struct disk_fmt disk;
		struct stat st;
		struct dl_task dl_task;

		dl_task.url_info = &url;
		dl_task.disk = &disk;

		if ((fid = open(url.file_path, O_RDONLY)) < 0) {
			send_404(socket_id);
			goto out;
		}
		if (fstat(fid, &st) < 0) {
			send_404(socket_id);
			goto cfid;
		}
		if (read(fid, &disk, sizeof(struct disk_fmt)) < 0 || disk.magic != DISK_MAGIC) {
			send_404(socket_id);
			goto cfid;
		}
		
		/* special case: flv */
		if (url.start != 0 && is_flv(url.filename, url.filenamelen)) {
			flv_send_data(fid, socket_id, &dl_task, &st);
			goto cfid;
		}

		/* special case: mp4 */
//		if (url.start != 0 && is_mp4(url.filename, url.filenamelen)) {
//			media_send_data(fid, socket_id, &dl_task, &st, "mp4");
//			goto cfid;
//		}

		/* normal case */
		if (url.range) {
			if (get_range(url.range, &dl_task.from, &dl_task.to, disk.filelen) < 0) {
				send_404(socket_id);
				goto cfid;
			}
			dl_task.total = disk.filelen;
			dl_task.use206 = 1;
		} else { /* url has start and end parameter */
			dl_task.from = 0;
			dl_task.to = disk.filelen;
			dl_task.total = disk.filelen;
			dl_task.use206 = 0;
		}

		/* 
		 * now we start to check if the request(dl_task) would be satisfied.
		 * but if the file has not beed finished and is not fresh, delete it
		 * and start to download, it is the download process's responsibility
		 * to fork a new process to feed the client request.
		 */
		if (st.st_size < sizeof(struct disk_fmt) + disk.headlen + disk.filelen &&
				!file_fresh(&st)) {
			close(fid);
			fid = -1;
			remove(url.file_path);
			stat_atom_inc(cache_stat, remove_scrap);
			download_data(&url, socket_id, gconf);
		} else if (range_satisfied(&dl_task, &disk, &st)) {
			send_data(fid, socket_id, &dl_task);
			stat_atom_inc(cache_stat, hit_success);
		} else {
			send_404(socket_id);
			stat_atom_inc(cache_stat, hit_404);
		}
	} else
	{
		if (!gconf->use_map)
			assign_path_prefix(&url, rnum, gconf);
		download_data(&url, socket_id, gconf);
	}

cfid:
	if (fid >= 0)
		close(fid);
out:
	return 0;
}

void daemon_chg()
{
	printf("entering daemon mode...\n");
	if (daemon(0, 0) < 0)
	{
		perror("daemon");
		_exit(0);
	}
}
size_t get_head( void *ptr, size_t size, size_t nmemb, void *userdata)
{
	struct curlblock *blk = (struct curlblock*)userdata;
	struct disk_fmt *disk = blk->disk;
	FILE *f = blk->f;
	unsigned int i;
	if (strncasecmp((char*)ptr, "HTTP/1.1 200 OK\r\n", 17) == 0 ||
			strncasecmp((char*)ptr, "HTTP/1.0 200 OK\r\n", 17) == 0)
		{
			if (fseek(f, sizeof(struct disk_fmt), SEEK_SET) < 0) {
				return 0;
			}

			blk->flag = BLK_F_200;
			return size*nmemb;
		}

	if (strncasecmp((char*)ptr, "HTTP/1.1 30", 11) == 0 ||
			strncasecmp((char*)ptr, "HTTP/1.0 30", 11) == 0)
		{
			blk->flag = BLK_F_302;
			return size*nmemb;
		}
	if (blk->flag == 0)
		return 0;
	if (blk->flag == BLK_F_302)
		return size*nmemb;

	if (sscanf((char*)ptr, "Content-Length: %u", &i) == 1) {
		disk->filelen = i;
		return size * nmemb;
	}

	/*
	 * we will drop these response line
	 */
	if (strncasecmp((char*)ptr, "Connection: ", 12) == 0) 
		return size * nmemb;
	if (strncasecmp((char*)ptr, "Date: ", 6) == 0) 
		return size * nmemb;
	if (strncasecmp((char*)ptr, "Last-Modified: ", 15) == 0) 
		return size * nmemb;

	/* save all other response line */
	if (fwrite(ptr, size, nmemb, f) < 0)
		return 0;
	disk->headlen += size * nmemb;

	/* 
	 * if it is the end line, save the disk_fmt to hard disk and start a process
	 * to feed the client request 
	 */
	if (strncmp((char*)ptr, "\r\n", 2) == 0) {
		if (!disk->filelen)
			return 0; /* we not support trunk mode */
		if (fseek(f, 0, SEEK_SET) < 0 || 
				fwrite(disk, sizeof(struct disk_fmt), 1, f) < 0 || 
				fflush(f) != 0 || fseek(f, sizeof(struct disk_fmt) + disk->headlen, SEEK_SET) < 0)
			return 0;
		/* 
		 * now start a new process to feed the client 
		 * but if it is range request, fail it, and 
		 * the download contineu
		 */
		if (blk->url->range)
			send_404(blk->sock);
		else {
			if (fork() == 0) {
				int fid = open(blk->url->file_path, O_RDONLY);
				if (fid >= 0) {
					struct dl_task dl_task;

					dl_task.url_info = blk->url;
					dl_task.disk = disk;
					dl_task.from = 0;
					dl_task.to = disk->filelen;
					dl_task.total = disk->filelen;
					dl_task.use206 = 0;

					sleep(1);
					send_data(fid, blk->sock,&dl_task);
					close(fid);
					close(blk->sock);
				} else send_404(blk->sock);
				_exit(0);
			}
		}
	}
	return size * nmemb;
}

// >0 nonblocking, 0 timeout, -1 error
int nonblocking_r(int fd, int timeout)
{
	struct timeval t = {timeout, 0};
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	select(fd+1, &fds, 0, 0, &t);
	return FD_ISSET(fd, &fds);
}
int nonblocking_w(int fd, int timeout)
{
	struct timeval t = {timeout, 0};
	fd_set fds;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	select(fd+1, 0, &fds, 0, &t);
	return FD_ISSET(fd, &fds);
}

/* @to point to the byte after the content */
int __send_data(int fid, int socket_id, off_t from, off_t to)
{
	int count = 4;
	ssize_t n;
	while (count > 0) {
		if (nonblocking_r(fid, FD_TIMEOUT) <= 0 || nonblocking_w(socket_id, FD_TIMEOUT) <= 0) {
			count --;
			continue;
		}
		n = sendfile(socket_id, fid, &from, to - from);
		if (n < 0)
			return 0;
		if (n == 0)
			count -- ;
		if (from >= to) {
			return 1;
		}
		last_send_time = time(0);
		sleep(2);
	}
	return 0;
}
void send_data(int fid, int socket_id, struct dl_task *dl_task){
	char http_head[1500] = {0};
	int http_head_i = 0;
	unsigned int len; /* point to first byte of real data */
	
	if(!dl_task->use206)
	{
		http_head_i += sprintf(http_head+http_head_i, "HTTP/1.1 200 Ok\r\n");
		http_head_i += sprintf(http_head+http_head_i, "Content-Length: %u\r\n", dl_task->total);
	} else
	{
		http_head_i += sprintf(http_head+http_head_i, "HTTP/1.1 206 Partial Content\r\n");
		http_head_i += sprintf(http_head+http_head_i, 
				"Content-Range: bytes %u-%u/%u\r\n", dl_task->from, dl_task->to-1, dl_task->total);
	}

	http_head_i += sprintf(http_head+http_head_i, "Path: %s\r\n", dl_task->url_info->file_path);
	http_head_i += sprintf(http_head+http_head_i, "Connection: close\r\n");

	if (send(socket_id,http_head, http_head_i,0) < 0) 
		return;
	len = sizeof(struct disk_fmt) + dl_task->disk->headlen;
	__send_data(fid, socket_id, sizeof(struct disk_fmt), len);
	__send_data(fid, socket_id, dl_task->from + len, dl_task->to + len);
}

//void media_send_data(int fd, int sock, struct dl_task *dl_task, struct stat *st, char *ext)
//{
//	struct disk_fmt *disk = dl_task->disk;
//	unsigned int start = dl_task->url_info->start;
//	unsigned int part0_len = sizeof(struct disk_fmt) + disk->headlen; /* data store in the disk_fmt struct and the headlen */
//	char head[1500];
//	int index = 0;
//
//	char start_time[20];
//	char input_file[40];
//	char output_file[20];
//	char *argv[10] = {"program", "-ss", start_time, "-i", input_file, 
//		"-vcodec", "copy", "-acodec", "copy", output_file};
//
//	index += sprintf(head + index, "HTTP/1.1 200 OK\r\n");
////	index += sprintf(head + index, "Content-Length: %u\r\n", st->st_size - part0_len);
//	index += sprintf(head + index, "Transfer-Encoding: chunked\r\n");
//	index += sprintf(head + index, "Path: %s\r\n", dl_task->url_info->file_path);
//	index += sprintf(head + index, "Connection: close\r\n");
//	if (send(sock, head, index, 0) < 0) 
//		return;
//	__send_data(fd, sock, sizeof(struct disk_fmt), part0_len); 
//
//	/* 
//	 * format the ffmpeg argv parameter 
//	 */
//	sprintf(start_time, "%d", start);
//	sprintf(input_file, "head:%d,%d.%s", fd, part0_len, ext); /* the ext is used for ffmpeg to guess the codec */
//	sprintf(output_file, "sock:%d.%s", sock, ext);
//	ffmpeg_convert(10, argv);
//	send(sock, "0\r\n\r\n", 5, 0);
//}
void flv_send_data(int fd, int sock, struct dl_task *dl_task, struct stat *st)
{
	struct disk_fmt *disk = dl_task->disk;
	unsigned int start = dl_task->url_info->start * 1000; /* millisenconds */
	struct FLVTag_t tag;
	unsigned int part0_len = sizeof(struct disk_fmt) + disk->headlen; /* data store in the disk_fmt struct and the headlen */
	unsigned int part1_len; /* from the real flv file */
	off_t offset = part0_len + FLV_SIZE_HEADER + FLV_SIZE_PREVIOUSTAG; 
	off_t from;


	unsigned int content_len;
	char head[1500] ;
	int index = 0;

	/* check if the file is corrent */
	if (!validateFLV(fd, st, part0_len))
		return ;
	if (lseek(fd, offset, SEEK_SET) < 0)
		return;

	/* 
	 * we get the first two key frame ,
	 * check whether there is a script tag 
	 */
	if (!readFLVTag(&tag, offset, fd))
		return ;
	if (tag.tagtype == FLV_TAG_SCRIPTDATA) {
		offset += tag.tagsize + FLV_SIZE_PREVIOUSTAG;
		if (!readFLVTag(&tag, offset, fd))
			return ;
		offset += tag.tagsize + FLV_SIZE_PREVIOUSTAG;
		if (!readFLVTag(&tag, offset, fd))
			return ;
		offset += tag.tagsize + FLV_SIZE_PREVIOUSTAG;
	} else {
		offset += tag.tagsize + FLV_SIZE_PREVIOUSTAG;
		if (!readFLVTag(&tag, offset, fd))
			return ;
		offset += tag.tagsize + FLV_SIZE_PREVIOUSTAG;
	}
	part1_len = offset - part0_len;

	/* now find which tag will send from */
	from = offset;
	while (readFLVTag(&tag, offset, fd) && tag.timestamp < start) {
		from = offset;
		offset += tag.tagsize + FLV_SIZE_PREVIOUSTAG;
	}

	/* we send data from @from to the end of file to the client */
	content_len = part1_len + (st->st_size - from);

	index += sprintf(head + index, "HTTP/1.1 200 OK\r\n");
	index += sprintf(head + index, "Content-Length: %u\r\n", content_len);
	index += sprintf(head + index, "Path: %s\r\n", dl_task->url_info->file_path);
	index += sprintf(head + index, "Connection: close\r\n");
	if (send(sock,head, index, 0) < 0) 
		return;
	__send_data(fd, sock, sizeof(struct disk_fmt),part0_len + part1_len);
	__send_data(fd, sock, from, st->st_size); /* done */
}

/* buff must at least SHA_DIGEST_LENGTH == 20 bytes */
void do_sha1(unsigned char *buff, char *name, int len, char *host, int hostlen)
{
	SHA_CTX sh1;
	SHA1_Init(&sh1);
	SHA1_Update(&sh1, name, len);
	SHA1_Update(&sh1, host, hostlen);
	SHA1_Final(buff, &sh1);
}

/* buff must at least SHA_DIGEST_LENGTH == 20 bytes */
int url_sha1(unsigned char *buff, struct url_info *url)
{
	int i1, i2, i3, i4;
	unsigned char none = 1;
	char *dot = index(url->url + 7, '.');
	char *slash = index(url->url + 7, '/');
	if (sscanf(url->url + 7, "%u.%u.%u.%u", &i1, &i2, &i3, &i4) == 4) {
		do_sha1(buff, url->filename, url->filenamelen, &none, 1);
		return 1;
	}
	if (dot && slash && slash > (dot + 1)) {
		dot ++;
		do_sha1(buff, url->filename, url->filenamelen, dot, slash - dot);
		return 1;
	}
	return 0;
}

/* 
 * buff is at least 43 bytes plus 1(zero terminated).
 * 43 = (SHA_DIGEST_LENGTH == 20) * 2 + 3 ( we insert 3 slash )
 * buff will be zero terminated
 */
int path_relative(unsigned char *buff, struct url_info *url)
{
	int i;
	int src = 0, dest = 0;
	unsigned char digest[20];
	if (!url_sha1(digest, url))
		return 0;
	buff += sprintf(buff, "/%02X/", digest[0]);
	buff += sprintf(buff, "%02X/", digest[1]);
	for (i=2; i<20; i++) {
		buff += sprintf(buff, "%02X", digest[i]);
	}
	*buff = 0;
	return 1;

}

int create_relative_path(struct url_info *url_info)
{
	int ret = 0;
	char *url;
	char *loc, *loc2;

	url_info->file_path_relative = url_info->path_buff+MAX_PATH_PREFIX;

	if (url_info->qmark)
		*url_info->qmark = 0;

	if (url_info->filename)
		ret = path_relative(url_info->file_path_relative, url_info);

	if (url_info->qmark)
		*url_info->qmark = '?';
	return ret;
}
char buff_404[] = "HTTP/1.1 404 Not Found\r\n"
                  "Content-Length: 0\r\n"
		  "Connection: Close\r\n"
                  "\r\n";
int buff_404_len = 0; // reset in main()
	
inline void send_404(int sock)
{
	send(sock, buff_404, buff_404_len, 0);
}
